﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.Text;

using NetStsAdm;
using NetStsAdm.NetStsAdmService;

namespace NetStsAdm.Commands
{
    /// <summary>
    /// Main command. This command rebuilds the stsadm command and send it to the service.
    /// If a file needs to be uploaded (-filename command argument) then the service is also
    /// called from there in order to send the fiel to the remote server.
    /// </summary>
    internal class StsAdmCommand : ICommand
    {
        List<String> fileNameExceptions;
        Dictionary<string, string> keyValues;
        NetStsAdmService.CommandExecuterClient service;

        public StsAdmCommand()
        {
            fileNameExceptions = new List<string>();
            fileNameExceptions.Add("backup");
            fileNameExceptions.Add("export");
        }

        string ServiceURL
        {
            get
            {
                if (!keyValues.ContainsKey(Constants.TARGETURL))
                {
                    if (Config.GetConfig().GetValue(Config.ConfigKeys.TargetURL) == "")
                        throw new ArgumentException();
                    else
                        return Config.GetConfig().GetValue(Config.ConfigKeys.TargetURL);
                }
                else
                {
                    string url = keyValues[Constants.TARGETURL];
                    keyValues.Remove(Constants.TARGETURL);
                    return url;
                }
            }
        }

        NetworkCredential Credential
        {
            get
            {
                string domain = string.Empty;
                string login = string.Empty;
                string pwd = string.Empty;

                // Get Login
                if (!keyValues.ContainsKey(Constants.TARGETLOGIN))
                {
                    if (Config.GetConfig().GetValue(Config.ConfigKeys.TargetLogin) == "")
                        return null;
                    login = Config.GetConfig().GetValue(Config.ConfigKeys.TargetLogin);
                }
                else
                {
                    login = keyValues[Constants.TARGETLOGIN];
                    keyValues.Remove(Constants.TARGETLOGIN);
                }

                // Get Pwd
                if (!keyValues.ContainsKey(Constants.TARGETPWD))
                {
                    pwd = Config.GetConfig().GetValue(Config.ConfigKeys.TargetPwd);
                }
                else
                {
                    pwd = keyValues[Constants.TARGETPWD];
                    keyValues.Remove(Constants.TARGETPWD);
                }

                // Extract domain if any
                if (login.IndexOf(@"\") > -1)
                {
                    domain = login.Substring(0, login.IndexOf(@"\"));
                    login = login.Substring(login.IndexOf(@"\") + 1);
                }

                if (domain == string.Empty)
                    return new NetworkCredential(login, pwd);
                else
                    return new NetworkCredential(login, pwd, domain);
            }
        }

        public int Execute(Dictionary<string, string> KeyValues, out string Output)
        {
            Output = "";
            keyValues = KeyValues;
            try
            {
                service = ServiceFactory.GetService(ServiceURL, Credential);
            }
            catch (ArgumentException)
            {
                return -2;
            }
            catch (Exception)
            {
                return -1;
            }

            // A file is specified in the command, we have first to upload it onto the
            // destination server. The method UploadFile will also update the parameter
            // value of the file path ...
            if (keyValues.ContainsKey("-filename"))
            {
                if (!fileNameExceptions.Contains(keyValues["-o"]))
                {
                    try
                    {
                        UploadFile();
                    }
                    catch (Exception ex)
                    {
                        Output = ex.Message;
                        return -1;
                    }
                }
            }

            // Rebuild the stsadm cmd before sending it to the service
            StringBuilder cmd = new StringBuilder("stsadm");
            foreach (string k in keyValues.Keys)
            {
                cmd.AppendFormat(" {0}", k);
                if (keyValues[k] != "")
                {
                    if (keyValues[k].Contains(" "))
                        cmd.AppendFormat(" \"{0}\"", keyValues[k]);
                    else
                        cmd.AppendFormat(" {0}", keyValues[k]);
                }
            }

            try
            {
                Output = service.ExecuteCommand(cmd.ToString());
                return 0;
            }
            catch (MessageSecurityException)
            {
                Output += Resource.ACCESS_DENIED;
                return -1;
            }
            catch (EndpointNotFoundException)
            {
                Output += Resource.SERVICE_NOT_RESPONDING;
                return -1;
            }
            catch (Exception ex)
            {
                Output = ex.Message;
                return -1;
            }
        }

        /// <summary>
        /// When a file needs to be pushed to the server, it is uploaded using the service
        /// </summary>
        void UploadFile()
        {           
            byte[] buffer;
            FileStream fileStream = new FileStream(keyValues["-filename"], FileMode.Open, FileAccess.Read);
            try
            {
                int length = (int)fileStream.Length;
                buffer = new byte[length];
                int count, sum = 0;

                // read until Read method returns 0 (end of the stream has been reached)
                while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
                    sum += count;
            }
            finally
            {
                fileStream.Close();
            }
            string fileName = Path.GetFileName(keyValues["-filename"]);
            string newFilePath = service.UploadFile(buffer, fileName);
            keyValues["-filename"] = newFilePath;
        }
    }
}